#pragma once
typedef unsigned int NSIZE;
typedef const unsigned char* NPCBYTE;
typedef unsigned char* NPBYTE;
typedef const char* NLPCSTR;
typedef const wchar_t*     NLPCWSTR;

#include <vector>
using namespace std;

class CBVHParser
{
public:
    enum ChannelType{
        Invalid     =   -1,
        Xposition,
        Yposition,
        Zposition,
        Xrotation,
        Yrotation,
        Zrotation,
        Channels
    };
    typedef float BVH_FLOAT;
    typedef BVH_FLOAT                   BVH_FRAME_SEGMENT[Channels];
    typedef BVH_FRAME_SEGMENT       *   PBVH_FRAME_SEGMENT;
    typedef BVH_FRAME_SEGMENT const *   PCBVHFRAME_SEGMENT;
    typedef struct _tagBVH_FRAME
    {
        NSIZE           dwSegments;
        BVH_FRAME_SEGMENT   vSegments[1];
    }BVH_FRAME;
    typedef BVH_FRAME       *   PBVH_FRAME;
    typedef BVH_FRAME const *   PCBVH_FRAME;

    class IDelegate
    {
    public:
        virtual ~IDelegate()
        {}
        virtual void    OnFrame (   PCBVH_FRAME             frame   )   =   0;
    };

    class CBVHJoint
    {
    public:
        CBVHJoint   (   NPCBYTE pbStart, 
                        NPCBYTE pbEnd   );
        ~CBVHJoint  (   );

        const vector<ChannelType>&    GetChannels   (   void    )   const
        {   return m_vChannels; }

        NSIZE       GetChannelsCount    (   void    )const
        {   return m_vChannels.size();  }

        ChannelType GetChannel          (   NSIZE szIndex    )   const
        {   return m_vChannels.at(szIndex);    }

        NSIZE       GetChildrenCount    (   void    )   const
        {   return m_vChildren.size();  }

        const CBVHJoint *   GetChild    (   NSIZE   szIndex )   const
        {   return m_vChildren.at(szIndex); }

        const CBVHJoint *   GetParent   (   void    )   const
        {   return m_pParentJoint;  }

        bool                HasSite     (   void    )   const
        {   return m_bHasSite;      }

        const double(&GetOffset(   void    )const) [3]
        {   return m_vOffset;   }

        const double(&GetSiteOffset(   void    )const) [3]
        {   return m_vSiteOffset;   }

        NSIZE   GetJointIndex   (   void    )   const
        {   return m_szJointIndex;  }

        const char* GetName (   void    )   const
        { return (const char*)m_szTag;   }

    protected:
        NPCBYTE ParseOffset     (   NPCBYTE         pbStart, 
                                    NPCBYTE         pbEnd,
                                    bool            bEndSite    );

        NPCBYTE ParseChannels   (   NPCBYTE         pbStart, 
                                    NPCBYTE         pbEnd       );

        void    AddChild        (   CBVHJoint   *   pChildJoint );

    private:
        friend CBVHParser;
        NSIZE                   m_szJointIndex;
        int                     m_nChannels;
        double                  m_vOffset[3];
        bool                    m_bHasSite;
        double                  m_vSiteOffset[3];
        NPBYTE                  m_szTag;
        vector<ChannelType>     m_vChannels;
        vector<CBVHJoint*>      m_vChildren;
        CBVHJoint           *   m_pParentJoint;
    };

    class CChannel
    {
    public:
        CChannel    (   ChannelType     type, 
                        CBVHJoint    *  pJoint  );

        ~CChannel   ();

    private:
        CBVHJoint       *   m_pJoint;
        ChannelType         m_eChannel;
    };

    CBVHParser(void);
    ~CBVHParser(void);

    bool                ParseFile           (   NLPCSTR         szFile      );

    bool                ParseFile           (   NLPCWSTR        szFile      );

    NSIZE               GetChannelsCount    (   void                        )   const;

    NSIZE               GetJointsCount      (   void                        )   const;

    const CChannel  *   GetChannel          (   NSIZE           nFrame      )   const;

    NSIZE               GetFrameCount       (   void                        )   const
    {   return m_vBVHFrame.size();  }

    double              GetFrameInterval    (   void                        )   const
    {   return m_dfFrameInterval;   }

    PCBVH_FRAME         GetFrame            (   NSIZE           nFrame      )   const;

    const CBVHJoint    *   GetRoot          (   void                        )   const;

    const CBVHJoint    *   GetJoint         (   NSIZE           nIndex      )   const;


    //  dynamic parse
    void                SetLineBreak        (   NLPCSTR         pbLineBreak );

    void                SetWordBreak        (   NLPCSTR         pbWorkBreak );

    bool                CachedParse         (   NPCBYTE         pbBuffer, 
                                                NSIZE           cbBuffer    );

    void                SetDelegate         (   IDelegate   *   pDelegate   );

private:
    bool        CacheIsEmpty        (   void    )   const;

    void        CacheBlock          (   NPCBYTE         pbBuffer,
                                        unsigned int    cbBuffer    );

    void        ClearCache          (   void    );

    NPCBYTE     DirectParse         (   NPCBYTE   pbBuffer,
                                        unsigned int    cbBuffer    )   const;

    NPCBYTE     DirectParse         (   NPCBYTE         pbStart,
                                        NPCBYTE         pbEnd       );

    void        ParseCache          (   void                        )   const;

    void        CachedParseInteral  (   NPCBYTE   pbBuffer,
                                        unsigned int    cbBuffer    );

    PBVH_FRAME  ParseFrame          (   NPCBYTE         pbStart,
                                        NPCBYTE         pbEnd       )   const;

    NPCBYTE     SkipLineBreak       (   NPCBYTE   pbBuffer,
                                        unsigned int    cbBuffer    )   const;

    NPCBYTE     ParseHierarchy      (   NPCBYTE         pbBuffer, 
                                        NSIZE           cbBuffer    );

    NPCBYTE     ParseJoints         (   NPCBYTE         pbStart, 
                                        NPCBYTE         pbEnd       );

    NSIZE       ParseFrames         (   NPCBYTE         pbStart, 
                                        NPCBYTE         pbEnd       );

    void        AddChannels         (   CBVHJoint   *   pJoint      );
private:
    vector<CChannel*>               m_vChannels;
    vector<CBVHJoint*>              m_vJoints;
    vector<PBVH_FRAME>              m_vBVHFrame;
    NSIZE                           m_nFrames;
    double                          m_dfFrameInterval;
    IDelegate                   *   m_pDelegate;
    char                        *   m_szLineBreak;
    char                        *   m_szWordBreak;
    //
    NPBYTE          m_pbCacheBuffer;
    unsigned int    m_cbCacheBuffer;
    unsigned int    m_cbCacheBufferPos;
};

